package com.sumer.brain.common.config;

import com.sumer.brain.common.security.filter.JwtAuthenticationFilter;
import com.sumer.brain.common.security.handler.CustomerAccessDeniedHandler;
import com.sumer.brain.common.security.handler.CustomerAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.savedrequest.NullRequestCache;

import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor
@Configuration
public class WebSecurityConfig {

    private final CustomerAuthenticationEntryPoint customerAuthenticationEntryPoint;

    private final CustomerAccessDeniedHandler customerAccessDeniedHandler;

    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    public static final List<String> IGNORE_URI = new ArrayList<>();

    public static final List<String> IGNORE_STATIC_URI = new ArrayList<>();

    static {
        IGNORE_URI.add("/login");
        IGNORE_URI.add("/captcha");
        IGNORE_URI.add("/register");
    }

    // 放行的静态资源
    static {
        IGNORE_STATIC_URI.add("/doc.html");
        IGNORE_STATIC_URI.add("/doc.html");
        IGNORE_STATIC_URI.add("/webjars/**");
        IGNORE_STATIC_URI.add("/webjars/");
        IGNORE_STATIC_URI.add("/v3/api-docs/swagger-config");
        IGNORE_STATIC_URI.add("/favicon.ico");
        IGNORE_STATIC_URI.add("/v3/api-docs");
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        /*
         * SecurityFilterChain一共有15个默认过滤器
         * 禁用URL重新编码:session的会话持有在客户端是用过cookie来保存sessionId来实现的，每次客户端的请求都携带sessionId。
         * 如果禁用了cookie，后端的默认响应会重写url将sessionId拼接到url后面，传递给页面，sessionId就在http访问日志中暴露了。
         * 1.DisableEncodeUrlFilter
         * web异步处理整合过滤器:
         * 2.WebAsyncManagerIntegrationFilter
         * 持有SecurityContent的过滤器:用于存储用户认证信息
         * 3.SecurityContextHolderFilter
         * 头信息写入过滤器
         * 4.HeaderWriterFilter
         * 5.CorsFilter
         * 6.CsrfFilter
         * 处理用户登出请求过滤器
         * 7.LogoutFilter
         * 用户名密码认证过滤器
         * 8.UsernamePasswordAuthenticationFilter
         * 默认登录页
         * 9.DefaultLoginPageGeneratingFilter
         * 默认登出页
         * 10.DefaultLogoutPageGeneratingFilter
         * 11.RequestCacheAwareFilter
         * 12.SecurityContextHolderAwareRequestFilter
         * 在经过该过滤器时候，如果还没有获得用户的认证信息，这创建一个匿名用户
         * 13.AnonymousAuthenticationFilter
         * 处理过滤器链中抛出来的权限校验异常
         * 14ExceptionTranslationFilter
         * 判断当前用户有没有权限访问目标资源
         * 15.AuthorizationFilter
         */
        // 禁用basic明文验证
        http.httpBasic(AbstractHttpConfigurer::disable);
        // 前后端分离架构不需要csrf防御
        http.csrf(AbstractHttpConfigurer::disable);
        // 禁用默认登出页
        http.logout(AbstractHttpConfigurer::disable);
        // 禁用默认登录页
        http.formLogin(AbstractHttpConfigurer::disable);
        // 无需给用户一个匿名身份
        http.anonymous(AbstractHttpConfigurer::disable);
        // 用于重定向，前后端分离项目无需重定向
        http.requestCache(cache -> cache.requestCache(new NullRequestCache()));
        // 前后端分离是无状态的不需要session，直接禁用
        http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        // authorizeHttpRequests:针对http请求进行授权配置
        // permitAll：具有所有权限,也就是可以匿名访问
        // anyRequest:任何请求,所有请求
        // authenticated: 认证(登录)
        http.authorizeHttpRequests(authorize -> authorize
//                 允许所有option请求
//                .requestMatchers(HttpMethod.OPTIONS,"/**").permitAll()
                // 允许匿名访问
                .requestMatchers(IGNORE_URI.toArray(new String[0])).permitAll()
                // 其他请求都需要认证
                .anyRequest().authenticated());

        http.exceptionHandling(e -> e
                // 认证异常
                .authenticationEntryPoint(customerAuthenticationEntryPoint)
                // 授权异常
                .accessDeniedHandler(customerAccessDeniedHandler));
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers(IGNORE_STATIC_URI.toArray(new String[0]));
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
}